package org.keyelements.hurda;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cz.mff.cuni.dpp.liboptions.DummyTest;
import cz.mff.cuni.dpp.liboptions.Option;
import cz.mff.cuni.dpp.liboptions.Options;
import cz.mff.cuni.dpp.liboptions.Result;
import cz.mff.cuni.dpp.liboptions.StringOption;
import cz.mff.cuni.dpp.liboptions.SwitchOption;

/**
 * Test pouziti knihovny z uplne jineho package, ukazuje, ze je to mozne.
 * 
 */
public class TimeTest {

	private static final Logger logger = LoggerFactory.getLogger(DummyTest.class);

	private static final String FORMAT_VALUE = "FORMAT-VALUE";
	private static final String OUTPUT_VALUE = "OUTPUT-VALUE";

	private static final String ARG01 = "ARG01";
	private static final String ARG02 = "ARG02";
	private static final String ARG03 = "ARG03";

	private Options options;

	private StringOption formatterOption;
	private StringOption outputOption;

	private SwitchOption portabilityOption;
	private SwitchOption appendOption;
	private SwitchOption verboseOption;
	private SwitchOption helpOption;
	private SwitchOption versionOption;

	private List<Option<?>> allOptions = new LinkedList<Option<?>>();

	private List<Option<?>> booleanOptions = new LinkedList<Option<?>>();

	@Before
	public void prepare() {
		logger.debug("prepare()");
		options = new Options();

		formatterOption = new StringOption.Builder("format", "FORMAT")
				.addAlias("f")
				.setDescription(
						"Specify output format, possibly overriding the format specified in the environment variable TIME.")
				.buildOption();

		outputOption = new StringOption.Builder("output", "FILE")
				.addAlias("o")
				.setDescription(
						"Do not send the results to stderr, but overwrite the specified file.")
				.buildOption();

		portabilityOption = new SwitchOption.Builder("portability").addAlias("p")
				.setDescription("Use the portable output format.").buildOption();

		appendOption = new SwitchOption.Builder("append").addAlias("a")
				.setDescription("(Used together with -o.) Do not overwrite but append.")
				.buildOption();

		verboseOption = new SwitchOption.Builder("verbose").addAlias("v")
				.setDescription("Give very verbose output about all the program knows about.")
				.buildOption();

		helpOption = new SwitchOption.Builder("help").setDescription(
				"Print a usage message on standard output and exit successfully.").buildOption();

		versionOption = new SwitchOption.Builder("version")
				.addAlias("V")
				.setDescription(
						"Print version information on standard output, then exit successfully.")
				.buildOption();

		options.addOptionDefinition(formatterOption);
		options.addOptionDefinition(outputOption);
		options.addOptionDefinition(portabilityOption);
		options.addOptionDefinition(appendOption);
		options.addOptionDefinition(verboseOption);
		options.addOptionDefinition(helpOption);
		options.addOptionDefinition(versionOption);

		allOptions.addAll(options.getOptions());

		booleanOptions.addAll(options.getOptions());
		booleanOptions.remove(formatterOption);
		booleanOptions.remove(outputOption);
	}

	public TimeTest() {
		logger.debug("TimeTest()");
	}

	@Test
	public void testAllShortPresent() {
		logger.debug("testAllShortPresent()");

		String[] commandLine = new String[] { "-f", FORMAT_VALUE, "-o", OUTPUT_VALUE, "-p", "-a",
				"-v", "-V", ARG01, ARG02 };

		Result result = options.process(commandLine);

		Assert.assertFalse(result.hasErrors());
		Assert.assertEquals(0, result.getErrors().getErrorsCount());

		// test ze to nespadne
		result.callCallbacks(false);
		result.callCallbacks(true);

		Assert.assertEquals(2, result.getArguments().size());
		Assert.assertTrue(result.getArguments().contains(ARG01));
		Assert.assertTrue(result.getArguments().contains(ARG02));

		Collection<Option<?>> usedOptions = new LinkedList<Option<?>>(allOptions);
		usedOptions.remove(helpOption);

		for (Option<?> option : usedOptions) {
			Assert.assertTrue("Option not present when should be: " + option.getPrimaryName(),
					result.isPresent(option));
		}

		Assert.assertEquals("Bad format value", FORMAT_VALUE, result.getValue(formatterOption));
		Assert.assertEquals("Bad outup value", OUTPUT_VALUE, result.getValue(outputOption));

		usedOptions.clear();
		usedOptions.addAll(booleanOptions);
		usedOptions.remove(helpOption);

		for (Option<?> option : usedOptions) {
			Assert.assertEquals("Bad boolean value for: " + option.getPrimaryName(), Boolean.TRUE,
					result.getValue(option));
		}
	}

	@Test
	public void testAllLongPresent() {
		logger.debug("testAllLongPresent()");

		String[] commandLine = new String[] { "--format=" + FORMAT_VALUE,
				"--output=" + OUTPUT_VALUE, "--portability", "--append", "--verbose", "--help",
				"--version", ARG01, ARG02, ARG03 };

		Result result = options.process(commandLine);

		Assert.assertFalse(result.hasErrors());
		Assert.assertEquals(0, result.getErrors().getErrorsCount());

		// test ze to nespadne
		result.callCallbacks(false);
		result.callCallbacks(true);

		Assert.assertEquals(3, result.getArguments().size());
		Assert.assertTrue(result.getArguments().contains(ARG01));
		Assert.assertTrue(result.getArguments().contains(ARG02));
		Assert.assertTrue(result.getArguments().contains(ARG03));

		Collection<Option<?>> usedOptions = new LinkedList<Option<?>>(allOptions);

		for (Option<?> option : usedOptions) {
			Assert.assertTrue("Option not present when should be: " + option.getPrimaryName(),
					result.isPresent(option));
		}

		Assert.assertEquals("Bad format value", FORMAT_VALUE, result.getValue(formatterOption));
		Assert.assertEquals("Bad outup value", OUTPUT_VALUE, result.getValue(outputOption));

		usedOptions.clear();
		usedOptions.addAll(booleanOptions);

		for (Option<?> option : usedOptions) {
			Assert.assertEquals("Bad boolean value for: " + option.getPrimaryName(), Boolean.TRUE,
					result.getValue(option));
		}

	}

	@Test
	public void testAllOptionsArgumets() {
		logger.debug("testAllOptionsArgumets()");

		String[] commandLine = new String[] { "--", "--format=" + FORMAT_VALUE,
				"--output=" + OUTPUT_VALUE, "--portability", "--append", "--verbose", "--help",
				"--version", "-f", FORMAT_VALUE, "-o", OUTPUT_VALUE, "-p", "-a", "-v", "-V" };

		Result result = options.process(commandLine);

		Assert.assertEquals(0, result.getErrors().getErrorsCount());
		Assert.assertFalse(result.hasErrors());

		// test ze to nespadne
		result.callCallbacks(false);
		result.callCallbacks(true);

		Assert.assertEquals(15, result.getArguments().size());
		Assert.assertTrue(result.getArguments().contains("-v"));
		Assert.assertTrue(result.getArguments().contains("--format=" + FORMAT_VALUE));
		Assert.assertTrue(!result.getArguments().contains("--"));

		Collection<Option<?>> usedOptions = new LinkedList<Option<?>>(allOptions);

		for (Option<?> option : usedOptions) {
			Assert.assertFalse("Option present when should not be: " + option.getPrimaryName(),
					result.isPresent(option));
		}

		for (Option<?> option : usedOptions) {
			Assert.assertEquals(
					"Value present for }not present (option): " + option.getPrimaryName(), null,
					result.getValue(option));
		}

	}

	@Test
	public void basicHelpTest() {
		logger.info("Without command:");
		options.printHelp();
		logger.info("With command:");
		options.printHelp("time [options] command [arguments...]");
	}

}
